package com.lagou.edu.factory;

import com.lagou.edu.myAnnotion.Autowired;
import com.lagou.edu.myAnnotion.Service;
import com.lagou.edu.myAnnotion.Transactional;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合） 任务二：对外提供获取实例对象的接口（根据id获取）
     */
    public static final ResourceBundle properties; // 全局配置
    private static Map<String, Object> map = new HashMap<String, Object>();  // 存储对象
    private static List<String> classPaths = new ArrayList<String>(); // 类所有文件
    private static List<Class<?>> clazzList = new ArrayList<Class<?>>();
    public static ConcurrentHashMap<String, String> clamap = new ConcurrentHashMap();
    public static ConcurrentHashMap methmaps = new ConcurrentHashMap();

    static {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        /*InputStream resourceAsStream = BeanFactory.class.getClassLoader()
                .getResourceAsStream("beans.xml");*/
        //加载配置文件找到要扫描的包
        properties = ResourceBundle.getBundle("application");
        String path = properties.getString("packages");
        if ("".equals(path) || null == path) {
            throw new RuntimeException("没配置扫描包");
        }
        scanPackage(path);
        for (Class<?> clazz : clazzList) {
            //parsingClass(mapper);
            per(clazz);
        }
        for (Class<?> clazz : clazzList) {
            parsingClass(clazz);
        }

        for (Class<?> clazz : clazzList) {
            //parsingClass(mapper);
            pp(clazz);
        }

        System.out.println("---");

        // 解析xml
       /* SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//bean");
            for (int i = 0; i < beanList.size(); i++) {
                Element element = beanList.get(i);
                // 处理每个bean元素，获取到该元素的id 和 class 属性
                String id = element.attributeValue("id");        // accountDao
                String clazz = element
                        .attributeValue("class");  // com.lagou.edu.dao.impl.JdbcAccountDaoImpl
                // 通过反射技术实例化对象
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();  // 实例化之后的对象

                // 存储到map中待用
                map.put(id, o);

            }

            // 实例化完成之后维护对象的依赖关系，检查哪些对象需要传值进入，根据它的配置，我们传入相应的值
            // 有property子元素的bean就有传值需求
            List<Element> propertyList = rootElement.selectNodes("//property");
            // 解析property，获取父元素
            for (int i = 0; i < propertyList.size(); i++) {
                Element element = propertyList
                        .get(i);   //<property name="AccountDao" ref="accountDao"></property>
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");

                // 找到当前需要被处理依赖关系的bean
                Element parent = element.getParent();

                // 调用父元素对象的反射功能
                String parentId = parent.attributeValue("id");
                Object parentObject = map.get(parentId);
                // 遍历父对象中的所有方法，找到"set" + name
                Method[] methods = parentObject.getClass().getMethods();
                for (int j = 0; j < methods.length; j++) {
                    Method method = methods[j];
                    if (method.getName().equalsIgnoreCase(
                            "set" + name)) {  // 该方法就是 setAccountDao(AccountDao accountDao)
                        method.invoke(parentObject, map.get(ref));
                    }
                }

                // 把处理之后的parentObject重新放到map中
                map.put(parentId, parentObject);

            }


        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }*/

    }

    private static void per(Class<?> clazz) {
        // 1.解析类上的注解
        String name = "";

        for (Annotation annotation : clazz.getAnnotations()) {
            if (annotation instanceof Service) {
                // 存储到map中待用.
                name = ((Service) annotation).value();
                if ("".equals(name)) {

                    name = clazz.getSimpleName().toLowerCase();
                }

                try {
                    Object object = clazz.newInstance();
                    map.put(name, object);

                    Class<?>[] cladd = clazz.getInterfaces();

                    if (null != cladd) {
                        System.out.println(cladd[0].getSimpleName());
                        for (int i = 0; i < cladd.length; i++) {
                            map.put(cladd[i].getSimpleName().toLowerCase(), object
                            );
                        }
                    }

                } catch (Exception e) {

                }
            }
        }
    }


    private static void pp(Class<?> clazz) {
        String name = "";
        for (Annotation annotation : clazz.getAnnotations()) {
            if (annotation instanceof Service) {
                name = ((Service) annotation).value();
                if ("".equals(name)) {
                    name = clazz.getSimpleName().toLowerCase();
                }
                // 2.解析方法上的注解
                Object o = map.get(name);
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(Transactional.class)) {
                        for (Annotation annotations : method.getDeclaredAnnotations()) {
                            if (annotations instanceof Transactional) {
                                //说明需要工厂方法创建代理类
                                ProxyFactory proxyFactory = (ProxyFactory) map
                                        .get(ProxyFactory.class.getSimpleName().toLowerCase());
                                if (null == clazz.getInterfaces()) {
                                    map.put(name, proxyFactory.getCglibProxy(o));
                                } else {
                                    Object os = proxyFactory.getJdkProxy(o);
                                    map.put(name, os);
                                    Class<?>[] cladd = clazz.getInterfaces();
                                    if (null != cladd) {
                                        for (int i = 0; i < cladd.length; i++) {
                                            map.put(cladd[i].getSimpleName().toLowerCase(),
                                                    proxyFactory.getJdkProxy(os));
                                        }
                                    }
                                }


                            }
                        }
                    }
                }
            }
        }

    }

    private static void parsingClass(Class<?> clazz) {

        String name = "";
        for (Annotation annotation : clazz.getAnnotations()) {
            if (annotation instanceof Service) {
                name = ((Service) annotation).value();
                if ("".equals(name)) {
                    name = clazz.getSimpleName().toLowerCase();
                }
                Object pard = map.get(name);

                //解析类属性上注解
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    Annotation[] allFAnnos = field.getAnnotations();
                    for (Annotation a : allFAnnos) {
                        if (a instanceof Autowired) {
                            Method[] methods = clazz.getDeclaredMethods();
                            for (int j = 0; j < methods.length; j++) {
                                Method method = methods[j];
                                String fildnam = field.getName();
                             /*   System.out.println(field.getName());
                                System.out.println(field.getType().getName());*/
                                fildnam = fildnam.substring(0, 1).toUpperCase() + fildnam
                                        .substring(1);
                              /*  System.out.println(fildnam);
                                System.out.println(field.getType().getSimpleName()
                                        .toLowerCase());
                                Object dds = map.get(field.getType().getSimpleName()
                                        .toLowerCase());
                                System.out.println(dds);*/
                                System.out.println("method--" + method.getName());
                                System.out.println(method.getName().equals("set" + fildnam));
                                if (method.getName().equalsIgnoreCase(
                                        "set" + fildnam)) {
                                    try {
                                        method.invoke(pard,
                                                map.get(field.getType().getSimpleName()
                                                        .toLowerCase()));
                                    } catch (Exception e) {

                                    }
                                }
                            }
                        }
                    }

                }
                try {
                    map.put(name, pard);
                } catch (Exception e) {

                }
            }
        }


    }


    /**
     * 根据全局配置文件的Mapper接口路径，扫描所有接口
     */
    private static void scanPackage(String mapperPath) {
        String classPath = Service.class.getResource("/").getPath();
        mapperPath = mapperPath.replace(".", "/");
        String mainPath = classPath + mapperPath;
        doPath(new File(mainPath));
        for (String className : classPaths) {
            className = className.replace(classPath.replace("/", "\\").replaceFirst("\\\\", ""), "")
                    .replace("\\", ".").replace(".class", "");
            if (className.contains("servlet")) {
                continue;
            }
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (!clazz.isInterface()) {
                clazzList.add(clazz);
            }

        }
    }

    /**
     * 获取文件或文件夹下所有的类.class
     */
    private static void doPath(File file) {
        // 文件夹，遍历
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1);
            }
        } else {
            // 文件，直接添加
            if (file.getName().endsWith(".class")) {
                classPaths.add(file.getPath());
            }
        }
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id.toLowerCase());
    }

}
